﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Actor.Net.Network
{
    public class ArrayBuffer : IDisposable
    {
        private ConcurrentQueue<List<ByteBuffer>> writeQueue { get; } = new ConcurrentQueue<List<ByteBuffer>>();
        private ConcurrentQueue<ByteBuffer> cacheQueue { get; } = new ConcurrentQueue<ByteBuffer>();
        private ConcurrentQueue<List<ByteBuffer>> cacheList { get; } = new ConcurrentQueue<List<ByteBuffer>>();
        private MemoryStream sendStream { get; } = new MemoryStream(8192);
        public int QueueCount => this.writeQueue.Sum(list => list.Count);

        public void Write(byte[] bytes,int offset, int count)
        {
            if (count - offset <= 0)
                return;

            if(!cacheList.TryDequeue(out List<ByteBuffer> list))
            {
                list = new List<ByteBuffer>();
            }

            while (true)
            {
                var lastCount = count - offset;
                if (lastCount <= 0)
                    break;

                if (!cacheQueue.TryDequeue(out ByteBuffer buffer))
                {
                    buffer = new ByteBuffer();
                }

                var copyCount = lastCount > ByteBuffer.Capacity ? ByteBuffer.Capacity : lastCount;
                Buffer.BlockCopy(bytes, offset, buffer.ByteArray, 0, copyCount);
                offset += copyCount;
                buffer.Length = copyCount;
                list.Add(buffer);
            }
            writeQueue.Enqueue(list);
        }

        public byte[] Get(out int count)
        {
            count = 0;
            if (writeQueue.Count == 0)
                return null;

            sendStream.Seek(0, SeekOrigin.Begin);
            sendStream.SetLength(0);
            while (writeQueue.TryDequeue(out List<ByteBuffer> list))
            {
                foreach(var buffer in list)
                {
                    sendStream.Write(buffer.ByteArray, 0, buffer.Length);
                    buffer.Length = 0;
                    cacheQueue.Enqueue(buffer);
                }
                list.Clear();
                cacheList.Enqueue(list);
            }
            count = (int)sendStream.Length;
            return sendStream.GetBuffer();
        }

        public void Dispose()
        {
            this.sendStream.Dispose();
        }
    }
}
